// Copyright 2007-2008 Rory Plaire (codekaizen@gmail.com)

using System;
using System.Collections.Generic;
using System.Windows.Media.Media3D;
using NUnit.Framework;
using AffineMatrixD = NPack.AffineMatrix<NPack.DoubleComponent>;
using VectorD = NPack.Vector<NPack.DoubleComponent>;
using IVectorD = NPack.Interfaces.IVector<NPack.DoubleComponent>;

namespace NPack.Tests.Wpf
{
    [TestFixture]
    public class Wpf3DAffineTransformTests
    {
        private static readonly Double _e = 0.00001;

        [Test]
        public void RotationTest()
        {
            Matrix3D m1 = new Matrix3D();
            Quaternion r = new Quaternion(new Vector3D(1, 0, 0), 45);
            m1.Rotate(r);

            AffineMatrixD a1 = new AffineMatrixD(MatrixFormat.RowMajor, 4);
            a1.RotateAlong(new VectorD(1, 0, 0), Math.PI / 4);

            Vector3D[] points = new Vector3D[]
                {
                    new Vector3D(1, 0, 0), 
                    new Vector3D(0, 1, 0), 
                    new Vector3D(0, 0, 1)
                };
            m1.Transform(points);

            VectorD[] npackPoints = new VectorD[]
                {
                    new VectorD(1, 0, 0, 1), 
                    new VectorD(0, 1, 0, 1), 
                    new VectorD(0, 0, 1, 1),
                };
            List<IVectorD> npackResults = new List<IVectorD>();
            npackResults.AddRange(a1.TransformVectors(npackPoints));

            Assert.AreEqual(m1.M11, (Single)a1[0, 0], _e);
            Assert.AreEqual(m1.M12, (Single)a1[0, 1], _e);
            Assert.AreEqual(m1.M13, (Single)a1[0, 2], _e);
            Assert.AreEqual(m1.M14, (Single)a1[0, 3], _e);

            Assert.AreEqual(m1.M21, (Single)a1[1, 0], _e);
            Assert.AreEqual(m1.M22, (Single)a1[1, 1], _e);
            Assert.AreEqual(m1.M23, (Single)a1[1, 2], _e);
            Assert.AreEqual(m1.M24, (Single)a1[1, 3], _e);

            Assert.AreEqual(m1.M31, (Single)a1[2, 0], _e);
            Assert.AreEqual(m1.M32, (Single)a1[2, 1], _e);
            Assert.AreEqual(m1.M33, (Single)a1[2, 2], _e);
            Assert.AreEqual(m1.M34, (Single)a1[2, 3], _e);

            Assert.AreEqual(m1.OffsetX, (Single)a1[3, 0], _e);
            Assert.AreEqual(m1.OffsetY, (Single)a1[3, 1], _e);
            Assert.AreEqual(m1.OffsetZ, (Single)a1[3, 2], _e);
            Assert.AreEqual(m1.M44, (Single)a1[3, 3], _e);

            Assert.AreEqual(points[0].X, (Single)npackResults[0][0], _e);
            Assert.AreEqual(points[0].Y, (Single)npackResults[0][1], _e);
            Assert.AreEqual(points[0].Z, (Single)npackResults[0][2], _e);
            Assert.AreEqual(points[1].X, (Single)npackResults[1][0], _e);
            Assert.AreEqual(points[1].Y, (Single)npackResults[1][1], _e);
            Assert.AreEqual(points[1].Z, (Single)npackResults[1][2], _e);
            Assert.AreEqual(points[2].X, (Single)npackResults[2][0], _e);
            Assert.AreEqual(points[2].Y, (Single)npackResults[2][1], _e);
            Assert.AreEqual(points[2].Z, (Single)npackResults[2][2], _e);
        }

        [Test]
        public void TranslateTest()
        {
            Matrix3D m1 = new Matrix3D();
            m1.Translate(new Vector3D(5, -10, 3));

            AffineMatrixD a1 = new AffineMatrixD(MatrixFormat.RowMajor, 4);
            a1.Translate(new VectorD(5, -10, 3));

            Point4D[] points = new Point4D[]
                {
                    new Point4D(1, 0, 0, 1), 
                    new Point4D(0, 1, 0, 1), 
                    new Point4D(0, 0, 1, 1)
                };
            m1.Transform(points);

            VectorD[] npackPoints = new VectorD[]
                {
                    new VectorD(1, 0, 0, 1), 
                    new VectorD(0, 1, 0, 1), 
                    new VectorD(0, 0, 1, 1),
                };
            List<IVectorD> npackResults = new List<IVectorD>();
			npackResults.AddRange(a1.TransformVectors(npackPoints));

			Assert.AreEqual(m1.M11, (Single)a1[0, 0], _e);
			Assert.AreEqual(m1.M12, (Single)a1[0, 1], _e);
			Assert.AreEqual(m1.M13, (Single)a1[0, 2], _e);
			Assert.AreEqual(m1.M14, (Single)a1[0, 3], _e);

			Assert.AreEqual(m1.M21, (Single)a1[1, 0], _e);
			Assert.AreEqual(m1.M22, (Single)a1[1, 1], _e);
			Assert.AreEqual(m1.M23, (Single)a1[1, 2], _e);
			Assert.AreEqual(m1.M24, (Single)a1[1, 3], _e);

			Assert.AreEqual(m1.M31, (Single)a1[2, 0], _e);
			Assert.AreEqual(m1.M32, (Single)a1[2, 1], _e);
			Assert.AreEqual(m1.M33, (Single)a1[2, 2], _e);
			Assert.AreEqual(m1.M34, (Single)a1[2, 3], _e);

			Assert.AreEqual(m1.OffsetX, (Single)a1[3, 0], _e);
			Assert.AreEqual(m1.OffsetY, (Single)a1[3, 1], _e);
			Assert.AreEqual(m1.OffsetZ, (Single)a1[3, 2], _e);
			Assert.AreEqual(m1.M44, (Single)a1[3, 3], _e);

            Assert.AreEqual(points[0].X, (Single)npackResults[0][0], _e);
            Assert.AreEqual(points[0].Y, (Single)npackResults[0][1], _e);
            Assert.AreEqual(points[0].Z, (Single)npackResults[0][2], _e);
            Assert.AreEqual(points[1].X, (Single)npackResults[1][0], _e);
            Assert.AreEqual(points[1].Y, (Single)npackResults[1][1], _e);
            Assert.AreEqual(points[1].Z, (Single)npackResults[1][2], _e);
            Assert.AreEqual(points[2].X, (Single)npackResults[2][0], _e);
            Assert.AreEqual(points[2].Y, (Single)npackResults[2][1], _e);
            Assert.AreEqual(points[2].Z, (Single)npackResults[2][2], _e);
        }

        [Test]
        public void ScaleTest()
        {
            Matrix3D m1 = new Matrix3D();
            m1.Scale(new Vector3D(100, -100, 33));

            AffineMatrixD a1 = new AffineMatrixD(MatrixFormat.RowMajor, 4);
            a1.Scale(new VectorD(100, -100, 33));

            Point4D[] points = new Point4D[]
                {
                    new Point4D(1, 0, 0, 1), 
                    new Point4D(0, 1, 0, 1), 
                    new Point4D(0, 0, 1, 1)
                };
            m1.Transform(points);

            VectorD[] npackPoints = new VectorD[]
                {
                    new VectorD(1, 0, 0, 1), 
                    new VectorD(0, 1, 0, 1), 
                    new VectorD(0, 0, 1, 1),
                };
            List<IVectorD> npackResults = new List<IVectorD>();
			npackResults.AddRange(a1.TransformVectors(npackPoints));

			Assert.AreEqual(m1.M11, (Single)a1[0, 0], _e);
			Assert.AreEqual(m1.M12, (Single)a1[0, 1], _e);
			Assert.AreEqual(m1.M13, (Single)a1[0, 2], _e);
			Assert.AreEqual(m1.M14, (Single)a1[0, 3], _e);

			Assert.AreEqual(m1.M21, (Single)a1[1, 0], _e);
			Assert.AreEqual(m1.M22, (Single)a1[1, 1], _e);
			Assert.AreEqual(m1.M23, (Single)a1[1, 2], _e);
			Assert.AreEqual(m1.M24, (Single)a1[1, 3], _e);

			Assert.AreEqual(m1.M31, (Single)a1[2, 0], _e);
			Assert.AreEqual(m1.M32, (Single)a1[2, 1], _e);
			Assert.AreEqual(m1.M33, (Single)a1[2, 2], _e);
			Assert.AreEqual(m1.M34, (Single)a1[2, 3], _e);

			Assert.AreEqual(m1.OffsetX, (Single)a1[3, 0], _e);
			Assert.AreEqual(m1.OffsetY, (Single)a1[3, 1], _e);
			Assert.AreEqual(m1.OffsetZ, (Single)a1[3, 2], _e);
			Assert.AreEqual(m1.M44, (Single)a1[3, 3], _e);

            Assert.AreEqual(points[0].X, (Single)npackResults[0][0], _e);
            Assert.AreEqual(points[0].Y, (Single)npackResults[0][1], _e);
            Assert.AreEqual(points[0].Z, (Single)npackResults[0][2], _e);
            Assert.AreEqual(points[1].X, (Single)npackResults[1][0], _e);
            Assert.AreEqual(points[1].Y, (Single)npackResults[1][1], _e);
            Assert.AreEqual(points[1].Z, (Single)npackResults[1][2], _e);
            Assert.AreEqual(points[2].X, (Single)npackResults[2][0], _e);
            Assert.AreEqual(points[2].Y, (Single)npackResults[2][1], _e);
            Assert.AreEqual(points[2].Z, (Single)npackResults[2][2], _e);
        }


        [Test]
        public void RotateAtTest()
        {
            Matrix3D m1 = new Matrix3D();
            m1.RotateAt(new Quaternion(new Vector3D(0, 0, 1), 180), new Point3D(10, 0, 0));

            AffineMatrixD a1 = new AffineMatrixD(MatrixFormat.RowMajor, 4);
            a1.RotateAt(new VectorD(10, 0, 0), new VectorD(0, 0, 1), Math.PI);

            Point4D[] points = new Point4D[]
                {
                    new Point4D(1, 0, 0, 1), 
                    new Point4D(0, 1, 0, 1), 
                    new Point4D(0, 0, 1, 1)
                };
            m1.Transform(points);

            VectorD[] npackPoints = new VectorD[]
                {
                    new VectorD(1, 0, 0, 1), 
                    new VectorD(0, 1, 0, 1), 
                    new VectorD(0, 0, 1, 1),
                };
            List<IVectorD> npackResults = new List<IVectorD>();
			npackResults.AddRange(a1.TransformVectors(npackPoints));

			Assert.AreEqual(m1.M11, (Single)a1[0, 0], _e);
			Assert.AreEqual(m1.M12, (Single)a1[0, 1], _e);
			Assert.AreEqual(m1.M13, (Single)a1[0, 2], _e);
			Assert.AreEqual(m1.M14, (Single)a1[0, 3], _e);

			Assert.AreEqual(m1.M21, (Single)a1[1, 0], _e);
			Assert.AreEqual(m1.M22, (Single)a1[1, 1], _e);
			Assert.AreEqual(m1.M23, (Single)a1[1, 2], _e);
			Assert.AreEqual(m1.M24, (Single)a1[1, 3], _e);

			Assert.AreEqual(m1.M31, (Single)a1[2, 0], _e);
			Assert.AreEqual(m1.M32, (Single)a1[2, 1], _e);
			Assert.AreEqual(m1.M33, (Single)a1[2, 2], _e);
			Assert.AreEqual(m1.M34, (Single)a1[2, 3], _e);

			Assert.AreEqual(m1.OffsetX, (Single)a1[3, 0], _e);
			Assert.AreEqual(m1.OffsetY, (Single)a1[3, 1], _e);
			Assert.AreEqual(m1.OffsetZ, (Single)a1[3, 2], _e);
			Assert.AreEqual(m1.M44, (Single)a1[3, 3], _e);

            Assert.AreEqual(points[0].X, (Single)npackResults[0][0], _e);
            Assert.AreEqual(points[0].Y, (Single)npackResults[0][1], _e);
            Assert.AreEqual(points[0].Z, (Single)npackResults[0][2], _e);
            Assert.AreEqual(points[1].X, (Single)npackResults[1][0], _e);
            Assert.AreEqual(points[1].Y, (Single)npackResults[1][1], _e);
            Assert.AreEqual(points[1].Z, (Single)npackResults[1][2], _e);
            Assert.AreEqual(points[2].X, (Single)npackResults[2][0], _e);
            Assert.AreEqual(points[2].Y, (Single)npackResults[2][1], _e);
            Assert.AreEqual(points[2].Z, (Single)npackResults[2][2], _e);
        }

        [Test]
        [Ignore]
        public void ShearTest()
        {
        }

        [Test]
        public void CombinedTransformsTest()
        {
            Matrix3D m1 = new Matrix3D();
            m1.Rotate(new Quaternion(new Vector3D(1, 0, 0), 135));
            m1.Translate(new Vector3D(1, 2, -3));
            m1.Scale(new Vector3D(-1, 3, 5));

            AffineMatrixD a1 = new AffineMatrixD(MatrixFormat.RowMajor, 4);
            a1.RotateAlong(new VectorD(1, 0, 0), Math.PI - Math.PI / 4);
            a1.Translate(new VectorD(1, 2, -3));
            a1.Scale(new VectorD(-1, 3, 5));

            Point4D[] points = new Point4D[]
                {
                    new Point4D(1, 0, 0, 1), 
                    new Point4D(0, 1, 0, 1), 
                    new Point4D(0, 0, 1, 1)
                };
            m1.Transform(points);

            VectorD[] npackPoints = new VectorD[]
                {
                    new VectorD(1, 0, 0, 1), 
                    new VectorD(0, 1, 0, 1), 
                    new VectorD(0, 0, 1, 1),
                };
            List<IVectorD> npackResults = new List<IVectorD>();
			npackResults.AddRange(a1.TransformVectors(npackPoints));

			Assert.AreEqual(m1.M11, (Single)a1[0, 0], _e);
			Assert.AreEqual(m1.M12, (Single)a1[0, 1], _e);
			Assert.AreEqual(m1.M13, (Single)a1[0, 2], _e);
			Assert.AreEqual(m1.M14, (Single)a1[0, 3], _e);

			Assert.AreEqual(m1.M21, (Single)a1[1, 0], _e);
			Assert.AreEqual(m1.M22, (Single)a1[1, 1], _e);
			Assert.AreEqual(m1.M23, (Single)a1[1, 2], _e);
			Assert.AreEqual(m1.M24, (Single)a1[1, 3], _e);

			Assert.AreEqual(m1.M31, (Single)a1[2, 0], _e);
			Assert.AreEqual(m1.M32, (Single)a1[2, 1], _e);
			Assert.AreEqual(m1.M33, (Single)a1[2, 2], _e);
			Assert.AreEqual(m1.M34, (Single)a1[2, 3], _e);

			Assert.AreEqual(m1.OffsetX, (Single)a1[3, 0], _e);
			Assert.AreEqual(m1.OffsetY, (Single)a1[3, 1], _e);
			Assert.AreEqual(m1.OffsetZ, (Single)a1[3, 2], _e);
			Assert.AreEqual(m1.M44, (Single)a1[3, 3], _e);

            Assert.AreEqual(points[0].X, (Single)npackResults[0][0], _e);
            Assert.AreEqual(points[0].Y, (Single)npackResults[0][1], _e);
            Assert.AreEqual(points[0].Z, (Single)npackResults[0][2], _e);
            Assert.AreEqual(points[1].X, (Single)npackResults[1][0], _e);
            Assert.AreEqual(points[1].Y, (Single)npackResults[1][1], _e);
            Assert.AreEqual(points[1].Z, (Single)npackResults[1][2], _e);
            Assert.AreEqual(points[2].X, (Single)npackResults[2][0], _e);
            Assert.AreEqual(points[2].Y, (Single)npackResults[2][1], _e);
            Assert.AreEqual(points[2].Z, (Single)npackResults[2][2], _e);
        }
    }
}
